package com.zhzh.common;


import com.zhzh.model.GlobalException;
import com.zhzh.model.ResultVO;
import com.zhzh.model.StatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;

public class UserCommonsUtil {

    private static final Logger log = LoggerFactory.getLogger(UserCommonsUtil.class);

    public static class MapUtils {


        /**
         * 拼接map
         */
        public static synchronized Map<String, Object> getParamMap(HttpServletRequest request) {
            Map<String, Object> resultMap = new HashMap<String, Object>();
            try {
                Map<String, String[]> paraMap = request.getParameterMap();
                Set<Entry<String, String[]>> set = paraMap.entrySet();
                Iterator<Entry<String, String[]>> it = set.iterator();
                while (it.hasNext()) {
                    String value = "";
                    Entry<String, String[]> entry = it.next();
                    for (String i : entry.getValue()) {
                        value = i;
                        break;
                    }
                    resultMap.put(entry.getKey(), value.trim());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return resultMap;
        }


        /**
         * 获取val
         */
        public static String getMapVal(Map<String, Object> paramMap, String key) {
            if (paramMap == null || key == null) {
                return "";
            }
            return paramMap.get(key) == null ? "" : paramMap.get(key).toString();
        }

    }


    public static class UuidUtil {
        /**
         * 获取uuid
         */
        public static String getUuid(int... maxLength) {
            String uuid = UUID.randomUUID().toString().replace("-", "");
            if (maxLength == null || maxLength.length == 0) {
                return uuid;
            }
            int size = maxLength[0] > 32 ? 32 : maxLength[0];
            uuid = uuid.substring(0, size);
            return uuid;
        }

        //根据指定长度生成纯数字的随机数
        public static String createData(int length) {
            StringBuilder sb = new StringBuilder();
            Random rand = new Random();
            for (int i = 0; i < length; i++) {
                sb.append(rand.nextInt(10));
            }
            String data = sb.toString();
            return data;
        }
    }

    /**
     * token工具类
     */
    public static class TokenUtil {

        //产生随机token
        public static String createToken(Integer userId) {
            String time ="userId="+userId +"-"+ new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
            return time + "-" + UuidUtil.getUuid(15);
        }

    }

        public static class Md5Util {

            private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5",
                    "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

            /**
             * 转换字节数组为16进制字串
             *
             * @param b 字节数组
             * @return 16进制字串
             */
            public static String byteArrayToHexString(byte[] b) {
                StringBuffer resultSb = new StringBuffer();
                for (int i = 0; i < b.length; i++) {
                    resultSb.append(byteToHexString(b[i]));
                }
                return resultSb.toString();
            }

        /**
         * 转换byte到16进制
         *
         * @param b
         * @return
         */
        private static String byteToHexString(byte b) {
            int n = b;
            if (n < 0) {
                n = 256 + n;
            }
            int d1 = n / 16;
            int d2 = n % 16;
            return hexDigits[d1] + hexDigits[d2];
        }

        /**
         * J 编码
         *
         * @param origin
         * @return
         */

        // MessageDigest 为 JDK 提供的加密类
        public static String MD5Encode(String origin) {
            origin = origin.trim();
            String resultString = null;
            try {
                resultString = new String(origin);
                MessageDigest md = MessageDigest.getInstance("md5");
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes("UTF-8")));
            } catch (Exception ex) {
            }
            return resultString;
        }

            public static void main(String[] args) {
                String s = "123456";
                System.out.println(Md5Util.MD5Encode(s));
            }

    }

    public static Map<String, Object> objectToMap(Object obj) {
        Map<String, Object> map = new HashMap<>();
        if (obj == null) {
            return map;
        }
        Class clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(obj));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }


    public static class EntityUtil{

        //实体类转map
        public static Map<String,Object> convertBeanToMap(Object bean) {
            Map<String,Object> returnMap = new HashMap<String, Object>();
            try{
                Class type = bean.getClass();
                BeanInfo beanInfo = Introspector.getBeanInfo(type);
                PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
                for (int i = 0; i < propertyDescriptors.length; i++) {
                    PropertyDescriptor descriptor = propertyDescriptors[i];
                    String propertyName = descriptor.getName();
                    if (!propertyName.equals("class")) {
                        Method readMethod = descriptor.getReadMethod();
                        Object result = readMethod.invoke(bean, new Object[0]);
                        if (result != null) {
                            returnMap.put(propertyName, result);
                        } else {
                            returnMap.put(propertyName, "");
                        }
                    }
                }
                return returnMap;
            }catch (Exception e){
                e.printStackTrace();
                return returnMap;
            }
        }

        /**
         * set属性的值到Bean
         */
        public synchronized static <T> T setFieldValue(Object bean, Map <String, Object> valMap){
            //基类
            Class <?> cls = bean.getClass();
            //version
            Class <?> super_cls = bean.getClass().getSuperclass();

            // 取出bean里的所有方法
            Method[] base_methods = cls.getDeclaredMethods();
            Method[] super_methods = super_cls.getDeclaredMethods();


            Field[] base_fields = cls.getDeclaredFields();
            Field[] super_fields = super_cls.getDeclaredFields();

            //合并
            Method[] methods = concatAll(base_methods, super_methods);
            Field[] fields = concatAll(base_fields, super_fields);
            for(Field field : fields){
                try{
                    String fieldSetName = parMedName("set", field.getName());
                    if(!checkSetMet(methods, fieldSetName)){
                        continue;
                    }
                    Method fieldSetMet = cls.getMethod(fieldSetName, field.getType());
                    String fieldKeyName = field.getName();
                    String value = valMap.get(fieldKeyName) == null ?"" : valMap.get(fieldKeyName).toString();
                    if(null != value && !"".equals(value)){
                        String fieldType = field.getType().getSimpleName();
                        if("String".equals(fieldType)){
                            fieldSetMet.invoke(bean, value);
                        }else if("Date".equals(fieldType)){
                            Date temp = parseDate(value);
                            fieldSetMet.invoke(bean, temp);
                        }else if("Integer".equals(fieldType) || "int".equals(fieldType)){
                            Integer intval = Integer.parseInt(value);
                            fieldSetMet.invoke(bean, intval);
                        }else if("Long".equalsIgnoreCase(fieldType)){
                            Long temp = Long.parseLong(value);
                            fieldSetMet.invoke(bean, temp);
                        }else if("Double".equalsIgnoreCase(fieldType)){
                            Double temp = Double.parseDouble(value);
                            fieldSetMet.invoke(bean, temp);
                        }else if("Boolean".equalsIgnoreCase(fieldType)){
                            Boolean temp = Boolean.parseBoolean(value);
                            fieldSetMet.invoke(bean, temp);
                        }else if("BigDecimal".equalsIgnoreCase(fieldType)){
                            BigDecimal bigDecimal = new BigDecimal(value);
                            fieldSetMet.invoke(bean, bigDecimal);
                        }else if("TimeStamp".equalsIgnoreCase(fieldType)){
                            fieldSetMet.invoke(bean, Timestamp.valueOf(value));
                        }else{
                        }
                    }
                }catch(Exception e){
                    continue;
                }
            }
            return (T) bean;
        }

        /**
         * 获取属性名数组
         */
        public static String[] getFiledName(Object o){
            Field[] fields = o.getClass().getDeclaredFields();
            String[] fieldNames = new String[fields.length];
            for(int i = 0; i < fields.length; i++){
                fieldNames[i] = fields[i].getName();
            }
            return fieldNames;
        }

        /**
         * 获取属性名,类型数组
         */
        public static Map <String, Object> getFiledNameType(Object o){
            Map <String, Object> map = new HashMap <String, Object>();
            Field[] fields = o.getClass().getDeclaredFields();
            for(int i = 0; i < fields.length; i++){
                map.put(fields[i].getName(), fields[i].getType());
            }
            return map;
        }

        public static String getFieldValueByFieldType(String fieldName, Object object){
            try{
                Field field = object.getClass().getField(fieldName);
                return field.getType().toString();
            }catch(Exception e){
                return "";
            }
        }


        /**
         * 返回参数放入request
         */
        public static void bean2Request(Object bean, HttpServletRequest req){
            // 基类
            Class <?> cls = bean.getClass();
            // version
            Class <?> super_cls = bean.getClass().getSuperclass();
            //
            // // 取出bean里的所有方法
            Method[] base_methods = cls.getDeclaredMethods();
            Method[] super_methods = super_cls.getDeclaredMethods();

            Field[] base_fields = cls.getDeclaredFields();
            Field[] super_fields = super_cls.getDeclaredFields();

            // 合并
            Method[] methods = concatAll(base_methods, super_methods);
            Field[] fields = concatAll(base_fields, super_fields);
            for(Field field : fields){
                try{
                    String fieldGetName = parMedName("get", field.getName());
                    if(!checkgetMet(methods, fieldGetName)){
                        continue;
                    }
                    Method fieldGetMet = cls.getMethod(fieldGetName);
                    String fieldKeyName = field.getName();
                    Object value = fieldGetMet.invoke(bean);
                    req.setAttribute(fieldKeyName, value);
                }catch(Exception e){
                    e.printStackTrace();
                    continue;
                }
            }
        }





        private static boolean checkgetMet(Method[] methods, String fieldGetMet){
            for(Method met : methods){
                if(fieldGetMet.equals(met.getName())){
                    return true;
                }
            }
            return false;
        }

        /**
         * 判断是否存在某属性的 set方法
         *
         * @return boolean
         */
        public synchronized static boolean checkSetMet(Method[] methods, String fieldSetMet){
            for(Method met : methods){
                if(fieldSetMet.equals(met.getName())){
                    return true;
                }
            }
            return false;
        }

        /**
         * 格式化string为Date
         *
         * @return date
         */
        public synchronized static Date parseDate(String datestr){
            if(null == datestr || "".equals(datestr)){
                return null;
            }
            try{
                String fmtstr = null;
                if(datestr.indexOf(':') > 0){
                    fmtstr = "yyyy-MM-dd HH:mm:ss";
                }else{
                    fmtstr = "yyyy-MM-dd";
                }
                SimpleDateFormat sdf = new SimpleDateFormat(fmtstr, Locale.UK);
                return sdf.parse(datestr);
            }catch(Exception e){
                return null;
            }
        }

        /**
         * 拼接在某属性的 set方法
         *
         * @return String
         */
        public synchronized static String parMedName(String par, String fieldName){
            if(null == fieldName || "".equals(fieldName)){
                return null;
            }
            int startIndex = 0;
            if(fieldName.charAt(0) == '_')
                startIndex = 1;
            return par + fieldName.substring(startIndex, startIndex + 1).toUpperCase() + fieldName.substring(startIndex + 1);
        }


        public static <T> T[] concatAll(T[] first, T[]... rest){
            int totalLength = first.length;
            for(T[] array : rest){
                totalLength += array.length;
            }
            T[] result = Arrays.copyOf(first, totalLength);
            int offset = first.length;
            for(T[] array : rest){
                System.arraycopy(array, 0, result, offset, array.length);
                offset += array.length;
            }
            return result;
        }

    }

    public static class StringUtil {

        /**
         * * 判断一个字符串是否为空串
         *
         * @param str String
         * @return true：为空 false：非空
         */
        public static boolean isEmpty(String str) {
            return isNull(str) || "".equals(str.trim());
        }

        /**
         * * 判断一个对象是否为空
         *
         * @param object Object
         * @return true：为空 false：非空
         */
        public static boolean isNull(Object object) {
            return object == null;
        }
    }

    public static class DateUtil{

        /**
         * 获取当前日期的 format 格式
         * @param format
         * @return String
         */
        public static final String dateTimeNow(final String format) {
            return parseDateToStr(format, new Date());
        }

        /**
         * 获取 date 的 format 格式
         * @param format
         * @param date
         * @return String
         */
        public static final String parseDateToStr(final String format, final Date date) {
            return new SimpleDateFormat(format).format(date);
        }

        /**
         * 获取当前日期, 默认格式为  yyyy-MM-dd
         *
         * @return String
         */
        public static String getDateYmd() {
            return dateTimeNow("yyyy-MM-dd");
        }

        /**
         * 获取当前日期, 默认格式为  yyyy-MM-dd HH:mm:ss
         * @return String
         */
        public static final String getDateYmdhms() {
            return dateTimeNow("yyyy-MM-dd HH:mm:ss");
        }

        /**
         * 获取当前日期, 默认格式为  yyyyMMdd
         *
         * @return String
         */
        public static String getDateYMD() {
            return dateTimeNow("yyyyMMdd");
        }
    }


    public static class fileUploadUtil{

        //文件后缀
        static String[] fileImg = new String[]{".jpg",".png",".gif",".jpeg"};
        //视频文件后缀
        static String[] fileVideo = new String[]{".mpg",".rmvb",".wmv",".3gp",".mp4",".mov",".avi",".flv"};
        //文件后缀
        static String[] fileTypes = new String[]{".jpg",".png",".gif",".jpeg",
                                ".doc",".xls",".docx",".xlsx",
                                ".rar",".pdf",".zip",".ppt",".pptx"};

        /**
         * 上传文件
         * @param file  文件
         * @param filePath 文件上传保存路径 例: D:/mulu/file/nmsc/
         * @param fileUrl 文件访问头地址   例: http://192.168.1.13:8999/nmsc/
         * @param type
         * @return ResultVO map
         */
        public static ResultVO handleFileUpload(MultipartFile file , String filePath , String fileUrl, String type) {
            log.info("文件上传");
            Map<String,Object> map=new HashMap<>();
            if(file==null){
                throw new GlobalException("文件不能为空");
            }
            // 获取旧文件名
            String fileName = file.getOriginalFilename();
            if(org.springframework.util.StringUtils.isEmpty(fileName)){
                throw new GlobalException("文件名有误");
            }
            log.info("上传的文件名为：" + fileName);
            // 获取文件的后缀名
            String suffixName = fileName.substring(fileName.lastIndexOf("."));
            log.info("上传的后缀名为：" + suffixName);
            Boolean status = false;
            String [] fileTypeo = type.equals("1")?fileTypes:type.equals("2")?fileImg:fileVideo;
            for (String fileType : fileTypeo) {
                if (fileType.equals(suffixName.toLowerCase())){
                    status = true;
                    break;
                }
            }
            if (!status) {
                throw new GlobalException("文件格式不正确");
            }
            String UUIDName = UUID.randomUUID().toString().replace("-", "")+suffixName; //新文件名
            // 文件上传后的路径
            File dest = new File(filePath + UUIDName);
            // 检测是否存在目录
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            try {
                file.transferTo(dest);
                //访问地址
                String fiUrl=fileUrl+UUIDName;
                map.put("oldFileName",fileName);
                map.put("picName",UUIDName);
                map.put("url",fiUrl);
                return new ResultVO(StatusCode.RESULT_SUCCESS, map);
            } catch (Exception e) {
                log.error("文件上传出错");
                e.printStackTrace();
            }
            return new ResultVO(StatusCode.SYSTEM_EXCEPTION);
        }


    }

    public static class IdCardUtil{

        /**
         * 身份证基础验证
         * @param idCard
         * @return boolean
         */
        public static boolean isCardId(String idCard){
            return Pattern.matches("^[1-9]\\d{5}(19|([22]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$",idCard);
        }

        /**
         * 根据身份证获取性别
         * @param idCard
         * @return
         */
        public static int getIdSex(String idCard){
            int sex = 2;
            if (Integer.parseInt(idCard.substring(16).substring(0, 1)) % 2 == 0) {
                sex = 1;
            } else {
                sex = 0;
            }
            return sex;
        }

        /**
         * 通过身份证获取被某人的生日
         * @param identifyNumber 身份证号
         * @return
         */
        private String getBirthDay(String identifyNumber) {
            String inputYear = identifyNumber.substring(6, 10);
            String inputMonth = identifyNumber.substring(10, 12);
            String inputDay = identifyNumber.substring(12, 14);
            String birthDay = inputYear + "-" + inputMonth + "-" + inputDay;
            return birthDay;
        }
    }

    /**
     * 雪花算法生成ID
     */
    public static class SnowflakeUtils {

        /**
         * 时间部分所占长度
         */
        private static final int TIME_LEN = 41;
        /**
         * 数据中心id所占长度
         */
        private static final int DATA_LEN = 5;
        /**
         * 机器id所占长度
         */
        private static final int WORK_LEN = 5;
        /**
         * 毫秒内序列所占长度
         */
        private static final int SEQ_LEN = 12;

        /**
         * 定义起始时间 2015-01-01 00:00:00
         */
        private static final long START_TIME = 1420041600000L;
        /**
         * 上次生成ID的时间截
         */
        private static long LAST_TIME_STAMP = -1L;
        /**
         * 时间部分向左移动的位数 22
         */
        private static final int TIME_LEFT_BIT = 64 - 1 - TIME_LEN;

        /**
         * 自动获取数据中心id（可以手动定义 0-31之间的数）
         */
        private static final long DATA_ID = getDataId();
        /**
         * 自动机器id（可以手动定义 0-31之间的数）
         */
        private static final long WORK_ID = getWorkId();
        /**
         * 数据中心id最大值 31
         */
        private static final int DATA_MAX_NUM = ~(-1 << DATA_LEN);
        /**
         * 机器id最大值 31
         */
        private static final int WORK_MAX_NUM = ~(-1 << WORK_LEN);
        /**
         * 随机获取数据中心id的参数 32
         */
        private static final int DATA_RANDOM = DATA_MAX_NUM + 1;
        /**
         * 随机获取机器id的参数 32
         */
        private static final int WORK_RANDOM = WORK_MAX_NUM + 1;
        /**
         * 数据中心id左移位数 17
         */
        private static final int DATA_LEFT_BIT = TIME_LEFT_BIT - DATA_LEN;
        /**
         * 机器id左移位数 12
         */
        private static final int WORK_LEFT_BIT = DATA_LEFT_BIT - WORK_LEN;

        /**
         * 上一次的毫秒内序列值
         */
        private static long LAST_SEQ = 0L;
        /**
         * 毫秒内序列的最大值 4095
         */
        private static final long SEQ_MAX_NUM = ~(-1 << SEQ_LEN);


        /**
         * 雪花算法生成ID,返回结果为long
         * @return
         */
        public synchronized static long getId() {
            long now = System.currentTimeMillis();

            //如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
            if (now < LAST_TIME_STAMP) {
                throw new RuntimeException(String.format("系统时间错误！ %d 毫秒内拒绝生成雪花ID！", START_TIME - now));
            }

            if (now == LAST_TIME_STAMP) {
                LAST_SEQ = (LAST_SEQ + 1) & SEQ_MAX_NUM;
                if (LAST_SEQ == 0) {
                    now = nextMillis(LAST_TIME_STAMP);
                }
            } else {
                LAST_SEQ = 0;
            }

            //上次生成ID的时间截
            LAST_TIME_STAMP = now;

            return ((now - START_TIME) << TIME_LEFT_BIT) | (DATA_ID << DATA_LEFT_BIT) | (WORK_ID << WORK_LEFT_BIT) | LAST_SEQ;
        }


        /**
         * 获取下一不同毫秒的时间戳，不能与最后的时间戳一样
         */
        public static long nextMillis(long lastMillis) {
            long now = System.currentTimeMillis();
            while (now <= lastMillis) {
                now = System.currentTimeMillis();
            }
            return now;
        }

        /**
         * 获取字符串s的字节数组，然后将数组的元素相加，对（max+1）取余
         */
        private static int getHostId(String s, int max) {
            byte[] bytes = s.getBytes();
            int sums = 0;
            for (int b : bytes) {
                sums += b;
            }
            return sums % (max + 1);
        }

        /**
         * 根据 host address 取余，发生异常就获取 0到31之间的随机数
         */
        public static int getWorkId() {
            try {
                return getHostId(Inet4Address.getLocalHost().getHostAddress(), WORK_MAX_NUM);
            } catch (UnknownHostException e) {
                return new Random().nextInt(WORK_RANDOM);
            }
        }

        /**
         * 根据 host name 取余，发生异常就获取 0到31之间的随机数
         */
        public static int getDataId() {
            try {
                return getHostId(Inet4Address.getLocalHost().getHostName(), DATA_MAX_NUM);
            } catch (UnknownHostException e) {
                return new Random().nextInt(DATA_RANDOM);
            }
        }


    }
}






